/*
 * DataTypes.cc
 *
 *  Created on: Mar 31, 2009
 *      Author: javier
 */

#include "Number.hh"
#include "DataTypes.hh"

#include "Domains.hh"
#include "Variable.hh"

#define DT_STATIC_MEMBERS(dataType,dtName) \
const std::string& dataType::NAME() { static std::string sl_name(#dtName); return sl_name; } \
const DataTypeId dataType::instance() { static dataType sl_instance; return sl_instance.getId(); }


namespace EUROPA
{

DT_STATIC_MEMBERS(VoidDT,void)
DT_STATIC_MEMBERS(FloatDT,float)
DT_STATIC_MEMBERS(IntDT,int)
DT_STATIC_MEMBERS(BoolDT,bool)
DT_STATIC_MEMBERS(StringDT,string)
DT_STATIC_MEMBERS(SymbolDT,symbol)


VoidDT::VoidDT()
    : DataType(NAME().c_str())
{
}

VoidDT::~VoidDT()
{
}

bool VoidDT::isNumeric() const { return false; }
bool VoidDT::isBool() const  { return false; }
bool VoidDT::isString() const  { return false; }

edouble VoidDT::createValue(const std::string&) const
{
  check_error(ALWAYS_FAILS, "can't create void value");
  return -1;
}

ConstrainedVariableId
VoidDT::createVariable(const ConstraintEngineId,
                                const Domain&,
                                const bool,
                                bool,
                                const std::string&,
                                const EntityId,
                                unsigned int) const
{
    check_error(ALWAYS_FAILS, "can't create void variable");
    return ConstrainedVariableId::noId();
}

FloatDT::FloatDT()
    : DataType(NAME().c_str())
{
    m_baseDomain = new IntervalDomain(getId());
}

FloatDT::~FloatDT()
{
}

bool FloatDT::isNumeric() const { return true; }
bool FloatDT::isBool() const  { return false; }
bool FloatDT::isString() const  { return false; }

edouble FloatDT::createValue(const std::string& value) const
{
  // TODO: simplify this
  if (value == "-inf" || value=="-inff") {
    return MINUS_INFINITY;
  }
  if (value == "inf" || value=="+inf" || value=="inff" || value=="+inff") {
    return PLUS_INFINITY;
  }
  return atof(value.c_str());
}

IntDT::IntDT()
    : DataType(NAME().c_str())
{
    m_minDelta = 1.0;
    m_baseDomain = new IntervalIntDomain(getId());
}

IntDT::~IntDT()
{
}

bool IntDT::isNumeric() const { return true; }
bool IntDT::isBool() const  { return false; }
bool IntDT::isString() const  { return false; }

edouble IntDT::createValue(const std::string& value) const
{
  if (value == "-inf") {
    return MINUS_INFINITY;
  }
  if (value == "+inf" || value == "inf") {
    return PLUS_INFINITY;
  }
  return atoi(value.c_str());
}

BoolDT::BoolDT()
    : DataType(NAME().c_str())
{
    m_minDelta = 1.0; // since we're saying that bool is numeric
    m_baseDomain = new BoolDomain(getId());
}

BoolDT::~BoolDT()
{
}

/*
 * To maintain compatibility with the original definition of the Bool domain in EUROPA :
 * BoolDT represents the integer interval [0,1]
 */
bool BoolDT::isNumeric() const { return true; }
bool BoolDT::isBool() const  { return true; }
bool BoolDT::isString() const  { return false; }

edouble BoolDT::createValue(const std::string& value) const
{
  if (value == "true") {
    return true;
  }
  if (value == "false") {
    return false;
  }
  check_error(ALWAYS_FAILS, "string value for boolean should be 'true' or 'false', not '" + value + "'");
  return -1;
}

std::string BoolDT::toString(edouble value) const
{
  static const std::string sl_true("true");
  static const std::string sl_false("false");
  checkError(value == true || value == false, value << "is not a bool value" );
  if(value == true)
    return sl_true;
  else
    return sl_false;
}

StringDT::StringDT()
    : DataType(NAME().c_str())
{
    m_baseDomain = new StringDomain(getId());
}

StringDT::~StringDT()
{
}

bool StringDT::isNumeric() const { return false; }
bool StringDT::isBool() const  { return false; }
bool StringDT::isString() const  { return true; }

edouble StringDT::createValue(const std::string& value) const
{
  return LabelStr(value);
}

SymbolDT::SymbolDT()
    : DataType(NAME().c_str())
{
    m_baseDomain = new SymbolDomain(getId());
}

SymbolDT::~SymbolDT()
{
}

bool SymbolDT::isNumeric() const { return false; }
bool SymbolDT::isBool() const  { return false; }
bool SymbolDT::isString() const  { return false; }

edouble SymbolDT::createValue(const std::string& value) const
{
  return LabelStr(value);
}

RestrictedDT::RestrictedDT(const std::string& name, const DataTypeId baseType,
                           const Domain& _baseDomain)
    : DataType(name)
    , m_baseType(baseType)
{
    m_baseDomain = _baseDomain.copy();
    m_baseDomain->setDataType(getId());
    setIsRestricted(true);
}

RestrictedDT::~RestrictedDT()
{
}

bool RestrictedDT::isNumeric() const { return m_baseType->isNumeric(); }
bool RestrictedDT::isBool() const  { return m_baseType->isBool(); }
bool RestrictedDT::isString() const  { return m_baseType->isString(); }
bool RestrictedDT::isEntity() const  { return m_baseType->isEntity(); }
edouble RestrictedDT::minDelta() const  { return m_baseType->minDelta(); }

edouble RestrictedDT::createValue(const std::string& value) const
{
    if (isNumeric())
        return(atof(value.c_str()));

    return(LabelStr(value));
}


}
